package com.jeasonzhao.web;

import java.util.Stack;

import com.jeasonzhao.commons.utils.RegexHelper;

public class JSBeautify
{
    public static enum TokenType
    {
        EOF,
        WORD,
        OPERATOR,
        START_EXPR,
        END_EXPR,
        START_BLOCK,
        END_BLOCK,
        SEMICOLON,
        BLOCK_COMMENT,
        COMMENT,
        STRING,
        UNKNOWN
    }

    public static enum TokenMode
    {
        DO_BLOCK,
        BLOCK,
        EXPRESSION,

    }

    public static enum TokenPrefix
    {
        NONE,
        NEWLINE,
        SPACE
    }

    public static class Token
    {
        public Token(String c,TokenType t)
        {
            this.Content = c;
            this.Type = t;
        }

        public String Content = null;
        public TokenType Type = TokenType.EOF;
    }

    private final static String INDENT = "    ";
    private final static String LINESTARTER =
        "|continue|try|throw|return|var|if"
        + "|switch|case|default|for|while|break|function|";
    private final static String WORDCHAR =
        "abcdefghijklmnopqrstuvwxyz"
        + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        + "0123456789_$";
    private final static String OPERATORS =
        "A+A-A*A/A%A&A++A--A=A+=A-=A*=A/="
        + "A%=A==A===A!=A!==A>A<A>=A<=A>>"
        + "A<<A>>>A>>>=A>>=A<<=A&&A&=A|A||"
        + "A!A!!A,A:A?A^A^=A|=A::A";

    private StringBuilder m_output;
    private int m_defaultIndentLevel;
    private String m_strInput;
    private int m_currentPosition;
    private Token m_lastToken;

    //Output
    private int m_currentIdentLevel;
    private Stack<TokenMode> m_modeStack;
    private TokenMode m_currentMode;
    private boolean m_doBlockJustClosed = true;
    private boolean m_isLineFlag = true;
    private Token m_currentToken = null;
    private boolean m_isCompatMode = true;
    private void printNewLine()
    {
        m_isLineFlag = false;
        if(m_output.length() < 1)
        {
            return;
        }
        if(m_isCompatMode == false)
        {
            if(m_output.charAt(m_output.length() - 1) != '\n')
            {
                m_output.append("\r\n");
            }
            for(int n = 0;n < m_currentIdentLevel;n++)
            {
                m_output.append(INDENT);
            }
        }
    }

    private void printSpace()
    {
        String last_output = " ";
        if(m_output.length() > 0)
        {
            last_output = "" + m_output.charAt(m_output.length() - 1);
        }
        if(last_output.equals(" ") == false
           && last_output.equals("\n") == false
           && last_output.equals(INDENT) == false)
        {
            m_output.append(' ');
        }
    }

    private void printToken()
    {
        m_output.append(m_currentToken.Content);
    }

    private void indent()
    {
        m_currentIdentLevel++;
    }

    private void unindent()
    {
        if(m_currentIdentLevel > 0)
        {
            m_currentIdentLevel--;
        }
    }

    private void remove_indent()
    {
        if(m_output.toString().endsWith(INDENT))
        {
            //??
            m_output.substring(0,m_output.length() - INDENT.length());
        }
    }

    private void setMode(TokenMode mode)
    {
        m_modeStack.push(m_currentMode);
        m_currentMode = mode;
    }

    private void restoreMode()
    {
        m_doBlockJustClosed = m_currentMode == TokenMode.DO_BLOCK;
        m_currentMode = m_modeStack.pop();
    }

    private boolean isTernaryOperation()
    {
        int level = 0;
        int colon_count = 0;
        for(int i = m_output.length() - 1;i >= 0;i--)
        {
            switch(m_output.charAt(i))
            {
                case ':':
                    if(level == 0)
                    {
                        colon_count++;
                    }
                    break;
                case '?':
                    if(level == 0)
                    {
                        if(colon_count == 0)
                        {
                            return true;
                        }
                        else
                        {
                            colon_count--;
                        }
                    }
                    break;
                case '{':
                    if(level == 0)
                    {
                        return false;
                    }
                    level--;
                    break;
                case '(':
                case '[':
                    level--;
                    break;
                case ')':
                case ']':
                case '}':
                    level++;
                    break;
            }
        }
        return false;
    }

    private char charAt(int n)
    {
        return m_strInput.charAt(n);
    }

    private boolean isElseCatchFinally(String s)
    {
        return "|else|catch|finally|".indexOf("|" + s + "|") >= 0;
    }

    private Token nextToken()
    {
        int newSpaceLines = 0;
        while(m_currentPosition < m_strInput.length()
              && Character.isWhitespace(charAt(m_currentPosition)))
        {
            if(charAt(m_currentPosition) == '\n')
            {
                newSpaceLines++;
            }
            m_currentPosition++;
        }
        if(m_currentPosition >= m_strInput.length())
        {
            return new Token(null,TokenType.EOF);
        }
        if(newSpaceLines > 1)
        {
            for(int i = 0;i < 2;i++)
            {
                printNewLine();
            }
        }
        boolean wanted_newline = (newSpaceLines == 1);
        char currentChar = charAt(m_currentPosition);
        m_currentPosition++;
        String content = "" + currentChar;
        if(WORDCHAR.contains("" + currentChar))
        {
            if(m_currentPosition < m_strInput.length())
            {
                while(WORDCHAR.contains("" + charAt(m_currentPosition)))
                {
                    content += charAt(m_currentPosition);
                    m_currentPosition++;
                    if(m_currentPosition == m_strInput.length())
                    {
                        break;
                    }
                }
            }

            if(m_currentPosition != m_strInput.length()
               && RegexHelper.matches("^[0-9]+[Ee]$","" + currentChar)
               && (charAt(m_currentPosition) == '-' || charAt(m_currentPosition) == '+'))
            {
                char sign = charAt(m_currentPosition);
                m_currentPosition++;
                Token t = nextToken();
                content += sign + t.Content;
                return new Token(content,TokenType.WORD);
            }

            if(content.equals("in"))
            {
                return new Token(content,TokenType.OPERATOR);
            }
            if(wanted_newline && m_lastToken.Type != TokenType.OPERATOR && !m_isLineFlag)
            {
                printNewLine();
            }
            return new Token(content,TokenType.WORD);

        }
        else if(currentChar == '(' || currentChar == '[')
        {
            return new Token(content,TokenType.START_EXPR);
        }
        else if(currentChar == ')' || currentChar == ']')
        {
            return new Token(content,TokenType.END_EXPR);
        }
        else if(currentChar == '{')
        {
            return new Token(content,TokenType.START_BLOCK);
        }
        else if(currentChar == '}')
        {
            return new Token(content,TokenType.END_BLOCK);
        }
        else if(currentChar == ';')
        {
            return new Token(content,TokenType.SEMICOLON);
        }
        else if(currentChar == '/')
        {
            String comment = "";
            if(charAt(m_currentPosition) == '*')
            {
                m_currentPosition++;
                if(m_currentPosition < m_strInput.length())
                {
                    while(!((charAt(m_currentPosition) == '*') && (charAt(m_currentPosition + 1) > '\0')
                            && (charAt(m_currentPosition + 1) == '/') && (m_currentPosition < m_strInput.length())))
                    {
                        comment += charAt(m_currentPosition);
                        m_currentPosition++;
                        if(m_currentPosition >= m_strInput.length())
                        {
                            break;
                        }
                    }
                }

                m_currentPosition += 2;
                return new Token("/*" + comment + "*/",TokenType.BLOCK_COMMENT);
            }

            else if(charAt(m_currentPosition) == '/')
            {
                comment = "" + currentChar;
                while((charAt(m_currentPosition) != '\n') && (charAt(m_currentPosition) != '\r'))
                {
                    comment += charAt(m_currentPosition);
                    m_currentPosition++;
                    if(m_currentPosition >= m_strInput.length())
                    {
                        break;
                    }
                }

                m_currentPosition++;
                if(wanted_newline)
                {
                    printNewLine();
                }
                return new Token(comment,TokenType.COMMENT);

            }
        }

        if((currentChar == '\'') || (currentChar == '\"') || ((currentChar == '/')
            && (
                (m_lastToken.Type == TokenType.WORD && m_lastToken.Content.equals("return"))
                || ((m_lastToken.Type == TokenType.START_EXPR)
                    || (m_lastToken.Type == TokenType.START_BLOCK)
                    || (m_lastToken.Type == TokenType.END_BLOCK)
                    || (m_lastToken.Type == TokenType.OPERATOR)
                    || (m_lastToken.Type == TokenType.EOF)
                    || (m_lastToken.Type == TokenType.SEMICOLON))))
            )
        {
            char sep = currentChar;
            boolean esc = false;
            String resulting_string = "" + currentChar;

            if(m_currentPosition < m_strInput.length())
            {
                if(sep == '/')
                {
                    boolean in_char_class = false;
                    while((esc) || (in_char_class) || (charAt(m_currentPosition) != sep))
                    {
                        resulting_string += charAt(m_currentPosition);
                        if(!esc)
                        {
                            esc = charAt(m_currentPosition) == '\\';
                            if(charAt(m_currentPosition) == '[')
                            {
                                in_char_class = true;
                            }
                            else if(charAt(m_currentPosition) == ']')
                            {
                                in_char_class = false;
                            }
                        }
                        else
                        {
                            esc = false;
                        }
                        m_currentPosition++;
                        if(m_currentPosition >= m_strInput.length())
                        {
                            return new Token(resulting_string,TokenType.STRING);
                        }
                    }
                }
                else
                {
                    while((esc) || (charAt(m_currentPosition) != sep))
                    {
                        resulting_string += charAt(m_currentPosition);
                        if(!esc)
                        {
                            esc = charAt(m_currentPosition) == '\\';
                        }
                        else
                        {
                            esc = false;
                        }
                        m_currentPosition++;
                        if(m_currentPosition >= m_strInput.length())
                        {
                            return new Token(resulting_string,TokenType.STRING);
                        }
                    }
                }
            }

            m_currentPosition += 1;

            resulting_string += sep;

            if(sep == '/')
            {
                // regexps may have modifiers /regexp/MOD , so fetch those, too
                while((m_currentPosition < m_strInput.length()) && (WORDCHAR.contains("" + charAt(m_currentPosition))))
                {
                    resulting_string += charAt(m_currentPosition);
                    m_currentPosition += 1;
                }
            }
            return new Token(resulting_string,TokenType.STRING);
        }

        if(currentChar == '#')
        {
            String sharp = "#";
            if(m_currentPosition < m_strInput.length() && Character.isDigit(charAt(m_currentPosition)))
            {
                do
                {
                    currentChar = charAt(m_currentPosition);
                    sharp += currentChar;
                    m_currentPosition += 1;
                }
                while((m_currentPosition < m_strInput.length()) && (currentChar != '#') && (currentChar != '='));
                if(currentChar == '#')
                {
                    return new Token(sharp,TokenType.WORD);
                }
                else
                {
                    return new Token(sharp,TokenType.OPERATOR);
                }
            }
        }

        if(currentChar == '<'
           && m_strInput.substring(m_currentPosition - 1,m_currentPosition + 3).equals("<!--")) //??3
        {
            m_currentPosition += 3;
            return new Token("<!--",TokenType.COMMENT);
        }
        if(currentChar == '-'
           && m_strInput.substring(m_currentPosition - 1,m_currentPosition + 2).equals("-->")) //??2
        {
            m_currentPosition += 2;
            if(wanted_newline)
            {
                printNewLine();
            }
            return new Token("-->",TokenType.COMMENT);
        }

        if(OPERATORS.contains("A" + currentChar + "A"))
        {
            while((m_currentPosition < m_strInput.length()) &&
                  OPERATORS.contains("A" + currentChar + charAt(m_currentPosition) + "A"))
            {
                content += charAt(m_currentPosition);
                m_currentPosition += 1;
                if(m_currentPosition >= m_strInput.length())
                {
                    break;
                }
            }

            return new Token(content,TokenType.OPERATOR);
        }
        return new Token(content,TokenType.UNKNOWN);
    }

    private JSBeautify()
    {
    }

    private String formatJavsScript(String sourceText,boolean isCompat)
    {
        if(null == sourceText)
        {
            return sourceText;
        }
        m_isCompatMode = isCompat;
        m_defaultIndentLevel = 0;
        m_output = new StringBuilder();
        m_modeStack = new Stack<TokenMode>();
        m_currentIdentLevel = m_defaultIndentLevel;
        m_doBlockJustClosed = false;
        m_currentMode = TokenMode.BLOCK;
        m_modeStack.push(m_currentMode);
        m_strInput = sourceText
            .replaceAll("<script type=\"text/javascript\">","")
            .replaceAll("</script>","")
            .replaceAll("<script>","");
        boolean add_script_tags = false;
        if(m_strInput.length() != sourceText.length())
        {
            m_output.append("<script type=\"text/javascript\">\r\n");
            add_script_tags = true;
        }

        m_lastToken = new Token("",TokenType.UNKNOWN);

        m_currentPosition = 0;

        String lastWord = "";
        boolean var_line = false;
        boolean var_line_tainted = false;
        boolean in_case = false;

        while(true)
        {
            m_currentToken = nextToken();
            if(m_currentToken.Type == TokenType.EOF)
            {
                break;
            }
            switch(m_currentToken.Type)
            {
                case START_EXPR:
                    var_line = false;
                    setMode(TokenMode.EXPRESSION);
                    if((m_lastToken.Content.equals(";")) || (m_lastToken.Type == TokenType.START_BLOCK))
                    {
                        printNewLine();
                    }
                    else if((m_lastToken.Type == TokenType.END_EXPR) || (m_lastToken.Type == TokenType.START_EXPR))
                    {
                        // do nothing on (( and )( and ][ and ]( ..
                    }
                    else if((m_lastToken.Type != TokenType.WORD) && (m_lastToken.Type != TokenType.OPERATOR))
                    {
                        printSpace();
                    }
                    else if(LINESTARTER.indexOf("|" + lastWord + "|") >= 0)
                    {
                        printSpace();
                    }
                    printToken();
                    break;
                case END_EXPR:
                    printToken();
                    restoreMode();
                    break;
                case START_BLOCK:

                    if(lastWord.equals("do"))
                    {
                        setMode(TokenMode.DO_BLOCK);
                    }
                    else
                    {
                        setMode(TokenMode.BLOCK);
                    }
                    if(m_lastToken.Type != TokenType.OPERATOR
                       && m_lastToken.Type != TokenType.START_EXPR)
                    {
                        if(m_lastToken.Type == TokenType.START_BLOCK)
                        {
                            printNewLine();
                        }
                        else
                        {
                            printSpace();
                        }
                    }
                    printToken();
                    indent();
                    break;
                case END_BLOCK:
                    if(m_lastToken.Type == TokenType.START_BLOCK)
                    {
                        unindent();
                    }
                    else
                    {
                        unindent();
                        printNewLine();
                    }
                    printToken();
                    restoreMode();
                    break;
                case WORD:

                    if(m_doBlockJustClosed)
                    {
                        // do {} ## while ()
                        printSpace();
                        printToken();
                        printSpace();
                        m_doBlockJustClosed = false;
                        break;
                    }

                    if(m_currentToken.Content.equals("case") || m_currentToken.Content.equals("default"))
                    {
                        if(m_lastToken.Content.equals(":"))
                        {
                            // switch cases following one another
                            remove_indent();
                        }
                        else
                        {
                            // case statement starts in the same line where switch
                            unindent();
                            printNewLine();
                            indent();
                        }
                        printToken();
                        in_case = true;
                        break;
                    }

                    TokenPrefix prefix = TokenPrefix.NONE;

                    if(m_lastToken.Type == TokenType.END_BLOCK) //}]
                    {
                        if(false == isElseCatchFinally(m_currentToken.Content))
                        {
                            prefix = TokenPrefix.NEWLINE;
                        }
                        else
                        {
                            prefix = TokenPrefix.SPACE;
                            printSpace();
                        }
                    }
                    else if(m_lastToken.Type == TokenType.SEMICOLON
                            && (m_currentMode == TokenMode.BLOCK || m_currentMode == TokenMode.DO_BLOCK)
                        )
                    {
                        prefix = TokenPrefix.NEWLINE;
                    }
                    else if(m_lastToken.Type == TokenType.SEMICOLON
                            && m_currentMode == TokenMode.EXPRESSION)
                    {
                        prefix = TokenPrefix.SPACE;
                    }
                    else if(m_lastToken.Type == TokenType.STRING)
                    {
                        prefix = TokenPrefix.NEWLINE;
                    }
                    else if(m_lastToken.Type == TokenType.WORD)
                    {
                        prefix = TokenPrefix.SPACE;
                    }
                    else if(m_lastToken.Type == TokenType.START_BLOCK)
                    {
                        prefix = TokenPrefix.NEWLINE;
                    }
                    else if(m_lastToken.Type == TokenType.END_EXPR)
                    {
                        printSpace();
                        prefix = TokenPrefix.NEWLINE;
                    }

                    if(m_lastToken.Type != TokenType.END_BLOCK
                       && isElseCatchFinally(m_currentToken.Content))
                    {
                        printNewLine();
                    }
                    else if(LINESTARTER.indexOf("|" + m_currentToken.Content + "|") >= 0
                            || prefix == TokenPrefix.NEWLINE)
                    {
                        if(m_lastToken.Content.equals("else"))
                        {
                            // no need to force newline on else break
                            printSpace();
                        }
                        else if(((m_lastToken.Type == TokenType.START_EXPR) || (m_lastToken.Content.equals("="))
                                 || (m_lastToken.Content.equals(","))) && (m_currentToken.Content.equals("function")))
                        {
                            // no need to force newline on "function": (function
                            // DONOTHING
                        }
                        else if((m_lastToken.Type == TokenType.WORD) && ((m_lastToken.Content.equals("return"))
                            || (m_lastToken.Content.equals("throw"))))
                        {
                            // no newline between "return nnn"
                            printSpace();
                        }
                        else if(m_lastToken.Type != TokenType.END_EXPR)
                        {
                            if(((m_lastToken.Type != TokenType.START_EXPR) || (m_currentToken.Content.equals("var") == false))
                               && (false == m_lastToken.Content.equals(":")))
                            {
                                // no need to force newline on "var": for (var x = 0...)
                                if(m_currentToken.Content.equals("if")
                                   && m_lastToken.Type == TokenType.WORD
                                   && lastWord.equals("else")
                                    )
                                {
                                    // no newline for } else if {
                                    printSpace();
                                }
                                else
                                {
                                    printNewLine();
                                }
                            }
                        }
                        else
                        {
                            if(LINESTARTER.indexOf("|" + m_currentToken.Content + "|") >= 0
                               && false == m_lastToken.Content.equals(")"))
                            {
                                printNewLine();
                            }
                        }
                    }
                    else if(prefix == TokenPrefix.SPACE)
                    {
                        printSpace();
                    }
                    printToken();
                    lastWord = m_currentToken.Content;

                    if(m_currentToken.Content.equals("var"))
                    {
                        var_line = true;
                        var_line_tainted = false;
                    }

                    if(m_currentToken.Content.equals("if") || m_currentToken.Content.equals("else"))
                    {
                        m_isLineFlag = true;
                    }

                    break;
                case SEMICOLON:
                    printToken();
                    var_line = false;
                    break;
                case STRING:

                    if(m_lastToken.Type == TokenType.START_BLOCK
                       || m_lastToken.Type == TokenType.END_BLOCK
                       || m_lastToken.Type == TokenType.SEMICOLON)
                    {
                        printNewLine();
                    }
                    else if(m_lastToken.Type == TokenType.WORD)
                    {
                        printSpace();
                    }
                    printToken();
                    break;
                case OPERATOR:
                    boolean start_delim = true;
                    boolean end_delim = true;
                    if(var_line && (m_currentToken.Content.equals(",") == false))
                    {
                        var_line_tainted = true;
                        if(m_currentToken.Content.equals(":"))
                        {
                            var_line = false;
                        }
                    }
                    if(var_line &&
                       m_currentToken.Content.equals(",")
                       && m_currentMode == TokenMode.EXPRESSION)
                    {
                        // do not break on comma, for(var a = 1, b = 2)
                        var_line_tainted = false;
                    }

                    if(m_currentToken.Content.equals(":") && in_case)
                    {
                        printToken(); // colon really asks for separate treatment
                        printNewLine();
                        in_case = false;
                        break;
                    }

                    if(m_currentToken.Content.equals("::"))
                    {
                        // no spaces around exotic namespacing syntax operator
                        printToken();
                        break;
                    }

                    if(m_currentToken.Content.equals(","))
                    {
                        if(var_line)
                        {
                            if(var_line_tainted)
                            {
                                printToken();
                                printNewLine();
                                var_line_tainted = false;
                            }
                            else
                            {
                                printToken();
                                printSpace();
                            }
                        }
                        else if(m_lastToken.Type == TokenType.END_BLOCK)
                        {
                            printToken();
                            printNewLine();
                        }
                        else
                        {
                            if(m_currentMode == TokenMode.BLOCK)
                            {
                                printToken();
                                printNewLine();
                            }
                            else
                            {
                                // EXPR od DO_BLOCK
                                printToken();
                                printSpace();
                            }
                        }
                        break;
                    }
                    else if((m_currentToken.Content.equals("--")) || (m_currentToken.Content.equals("++")))
                    { // unary operators special case
                        if(m_lastToken.Content.equals(";"))
                        {
                            if(m_currentMode == TokenMode.BLOCK)
                            {
                                // { foo; --i }
                                printNewLine();
                                start_delim = true;
                                end_delim = false;
                            }
                            else
                            {
                                // space for (;; ++i)
                                start_delim = true;
                                end_delim = false;
                            }
                        }
                        else
                        {
                            if(m_lastToken.Content.equals("{"))
                            {
                                // {--i
                                printNewLine();
                            }
                            start_delim = false;
                            end_delim = false;
                        }
                    }
                    else if(((m_currentToken.Content.equals("!")) || (m_currentToken.Content.equals("+"))
                             || (m_currentToken.Content.equals("-"))) && ((m_lastToken.Content.equals("return"))
                        || (m_lastToken.Content.equals("case"))))
                    {
                        start_delim = true;
                        end_delim = false;
                    }
                    else if(((m_currentToken.Content.equals("!")) || (m_currentToken.Content.equals("+")) || (m_currentToken.Content.equals("-")))
                            && (m_lastToken.Type == TokenType.START_EXPR))
                    {
                        // special case handling: if (!a)
                        start_delim = false;
                        end_delim = false;
                    }
                    else if(m_lastToken.Type == TokenType.OPERATOR)
                    {
                        start_delim = false;
                        end_delim = false;
                    }
                    else if(m_lastToken.Type == TokenType.END_EXPR)
                    {
                        start_delim = true;
                        end_delim = true;
                    }
                    else if(m_currentToken.Content.equals("."))
                    {
                        // decimal digits or object.property
                        start_delim = false;
                        end_delim = false;

                    }
                    else if(m_currentToken.Content.equals(":"))
                    {
                        if(isTernaryOperation())
                        {
                            start_delim = true;
                        }
                        else
                        {
                            start_delim = false;
                        }
                    }
                    if(start_delim && m_isCompatMode == false)
                    {
                        printSpace();
                    }

                    printToken();

                    if(end_delim && m_isCompatMode == false)
                    {
                        printSpace();
                    }
                    break;
                case BLOCK_COMMENT:

                    printNewLine();
                    printToken();
                    printNewLine();
                    break;

                case COMMENT:

                    // print_newline();
                    printSpace();
                    printToken();
                    printNewLine();
                    break;

                case UNKNOWN:
                    printToken();
                    break;
            }
            m_lastToken = m_currentToken;
        }
        if(add_script_tags)
        {
            m_output.append("\r\n").append("</script>");
        }
        return m_output.toString();
    }

    public static String beautify(String str)
    {
        JSBeautify b = new JSBeautify();
        return b.formatJavsScript(str,false);
    }

    public static String compat(String str)
    {
        JSBeautify b = new JSBeautify();
        return b.formatJavsScript(str,true);
    }

    public static void main(String[] argvs)
    {
        String str = "/*AAA*/<!--CCC-->window.combotreedata_83c7e2f201b9382ed1bc2358d3078ebc=[{id:-1,text:\"Folder1\",iconCls:\"icon-ok\",children:[{id:2,text:\"File1\",checked:true},{id:3,text:\"Folder2\",state:\"open\",children:[{id:4,text:\"File3\",attributes:{p1:\"value1\",p2:\"value2\"},checked:true,iconCls:\"icon-reload\"},{id:8,text:\"Async Folder\",state:\"closed\"}]}]},{text:\"Languages\",state:\"closed\",children:[{id:\"j1\",text:\"Java\"},{id:\"j2\",text:\"C#\"}]}];";
        String str2 = "if('this_is'==/an_example/){do_something();}else{var a=b?(c%d):e[f];} do{a(xx)}while(1==1);";
        System.out.println(JSBeautify.beautify(str));
        System.out.println(JSBeautify.compat(str2));
    }
}
